'''
commander.py

Copyright 2009 Xavier Mendez Navarro aka Javi

This file is part of pysqlin

pysqlin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

pysqlin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pysqlin; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
'''
from framework.roles import Registrant
from framework.myexceptions import MyException

import xml.dom.minidom
from xml.dom.minidom import Node
import logging

class CommandParser(Registrant):
    def __init__(self, filename):
	self.__logger = logging.getLogger("ui.CommandParser")

	try:
	    self.commands = self.read_xml(filename)
	except Exception ,e:
	    raise MyException('Error reading XML')

    # ------------------------------------------------
    # 
    # ------------------------------------------------
    def goto_previous(self, current):
	if current == 'root': 
	    return self.commands

	node_sections = current.split('.')

	go_to = self.commands
	for i in node_sections[1:-1]:
	    go_to = go_to['sections'][i]

	return go_to

    def goto_section(self, section):
	if section == 'root': 
	    return self.commands

	previous = self.goto_previous(section)
	name = section.split('.')[-1]
	if previous['sections'].has_key(name):
	    return previous['sections'][name]
	return None

    def __create_command(self, full_section, command_name, help, usage, parameters, label = None):
        """ Create a new command

        @param command_name 
        @param help 
        @param parameters [ (<name>, (<value1>, <value2)), ... ]
        @param label 
        """
	self.__logger.debug('__create_command. command_name=%s,parameters=%s' % (command_name, parameters))

	command_node = {
		'name': command_name,
		'help': help,
		'usage': usage,
		'parameters': {},
	}

	if label:
	    command_node['label'] = label

	n = 0
	for param_name, values, default in parameters:
	    n = n + 1

	    lvalues = []
	    for value in values:
		if value == '{@event@}':
			lvalues = '{@event@}'
			event = "PARAMETER %s.%s.%s" % (full_section, command_name, param_name)
			self.create_event(event)
			self.__logger.debug("__create_command. Data event: %s", event)
			break
		elif value.startswith('{@KB:'):
			lvalues = value
			self.__logger.debug("__create_command. kbase item: %s", value)
			break
		lvalues.append(value)

	    command_node['parameters'][n] = {
		'name': param_name,
		'values': lvalues
	    }
	    if default:
		command_node['parameters'][n]['default'] = default

	event = "COMMAND %s.%s" % (full_section, command_name)
	self.create_event(event)
	self.__logger.debug("__create_command. Command:%s, event: %s" % (command_node, event))

	return command_node

    def add_command(self, full_section, command_name, help, usage, parameters, label = None):
        """ Adds a new command

        @param full_section
        @param command_name 
        @param help 
        @param parameters [ (<name>, (<value1>, <value2)), ... ]
        @param label 
        """
	self.__logger.debug('add_command. full_section=%s, command_name=%s,parameters=%s' % (full_section, command_name, parameters))

	node = self.goto_section(full_section)

	if node is None: return

	command_node = self.__create_command(full_section, command_name, help, usage, parameters, label)

	node['commands'][command_name] = command_node

    def delete_commands(self, full_section, label):
	node = self.goto_section(full_section)
	if node is None:
	    return

	for cmd_name in [cmd_name for cmd_name in node['commands'].keys() if node['commands'][cmd_name].has_key('label') and  node['commands'][cmd_name]['label'] == label]:
	    del node['commands'][cmd_name]
		
    # ------------------------------------------------
    # XML parsing
    # ------------------------------------------------
    def get_text(self, el):
	rc = ""
	for node in el.childNodes:
	    if node.nodeType == node.TEXT_NODE:
		rc = rc + node.data
	return rc.strip()

    def parse_command(self, section_name, nodecommand):
	command = {}

	command_name = nodecommand.getAttribute("name")
	help = self.get_text(nodecommand.getElementsByTagName("help")[0])
	usage = self.get_text(nodecommand.getElementsByTagName("usage")[0])

	parameters = []
	default = None
	for nodeparams in nodecommand.getElementsByTagName("param"):
	    name = nodeparams.getAttribute("name")
	    default = nodeparams.getAttribute("default")

	    lvalues = []
	    for nodevalues in nodeparams.getElementsByTagName("value"):
		t = self.get_text(nodevalues)
		lvalues.append(t)
	    parameters.append((name, lvalues, default))

	return self.__create_command(section_name, command_name, help, usage, parameters)

    def parse_section(self, parent_name, node):
	section_name = node.getAttribute("name")
	help = self.get_text(node.getElementsByTagName("help")[0])
	fullname = parent_name + "." + section_name

	section = {
	    'name': section_name,
	    'fullname': fullname,
	    'help': help,
	    'commands': {},
	    'sections': {}
	}

	#for nodecommand in node.getElementsByTagName("command"):
	for nodecommand in [e for e in node.childNodes if e.nodeName == 'command']:
	    xmlcommand = self.parse_command(fullname, nodecommand)
	    section['commands'][xmlcommand['name']] = xmlcommand

	for n in [e for e in node.childNodes if e.nodeName == 'section']:
	    section_node = self.parse_section(fullname, n)
	    section['sections'][section_node['name']] = section_node

	return section

    def read_xml(self, filename):
    	''' Reads xml filename and constructs the following data structure:

	    {
		'name': 'root',
		'sections': {
		    'oracle': 
		    {
			'fullname': 'root.oracle',
			'name': 'oracle',
			'help': 'Enter database Oracle SQL Injection methods',
			'commands': 
			{
				'columns':
				{
					'name': 'columns',
					'help': 'Get table\'s columns names.',
					'usage': 'columns <table_name> <row>',
					'parameters':
					{
						1:
						{
							'name': 'row',
							'values': []
							'default': '0-'
						},
						2:
						{
							'name': 'table_name',
							'values': ['4','5','6']
						},
					}
				}
			}
		    },
		}
	    }
	'''

	doc = xml.dom.minidom.parse(filename)

	options = {
		'name': 'root',
		'fullname': 'root',
		'sections': {},
		'commands': {}
	}

	# root commands
	for nodecommand in filter(lambda x: x.nodeName == 'command', doc.documentElement.childNodes):
		xmlcommand = self.parse_command('root', nodecommand)
		options['commands'][xmlcommand['name']] = xmlcommand

	#for node in doc.getElementsByTagName("section"):
	for node in filter(lambda x: x.nodeName == 'section', doc.documentElement.childNodes):
	    section = self.parse_section('root', node)
	    options['sections'][section['name']] = section

	self.__logger.debug('read_xml. options=%s', options)
	return options


